<!--
 Copyright (c) 2016,2017,2018,2019,2020,2021,2022 Klaus Landsdorf (http://node-red.plus/)
 All rights reserved.
 node-red-contrib-modbus - The BSD 3-Clause License

 @author <a>Andrea Verardi</a> (Anversoft)
-->

<script type="text/javascript">
  //Modbus sequences
  var functions = [{
      v: "FC1",
      t: "FC 1: Read Coil Status"
    },
    {
      v: "FC2",
      t: "FC 2: Read Input Status"
    },
    {
      v: "FC3",
      t: "FC 3: Read Holding Registers"
    },
    {
      v: "FC4",
      t: "FC 4: Read Input Registers"
    },
  ];


  RED.nodes.registerType('modbus-flex-sequencer', {
    category: 'modbus',
    color: '#E9967A',
    defaults: {
      name: {
        value: ''
      },
      sequences: {
        value: [{
          name: null,
          unitid: null,
          fc: functions[0].v,
          address: null,
          quantity: null
        }],
        validate: function (sequences) {
          if (!sequences || sequences.length === 0) {
            return false;
          }

          for (var i = 0; i < sequences.length; i++) {
            var r = sequences[i];
            if (!r.unitid || isNaN(parseInt(r.unitid, 10)) || !r.address || isNaN(parseInt(r.address, 10)) || !r
              .quantity || isNaN(parseInt(r.quantity, 10))) {
              return false;
            }
          }

          return true;
        }
      },
      server: {
        type: 'modbus-client',
        required: true
      },
      showStatusActivities: {
        value: false
      },
      showErrors: {
        value: false
      },
      showWarnings: {
        value: true
      },
      logIOActivities: {
        value: false
      },
      useIOFile: {
        value: false
      },
      ioFile: {
        value: '',
        type: 'modbus-io-config',
        required: false
      },
      useIOForPayload: {
        value: false
      },
      emptyMsgOnFail: {
        value: false
      },
      keepMsgProperties: {
        value: false
      },
      delayOnStart: {
        value: false
      },
      startDelayTime: {
        value: ''
      },
    },
    inputs: 1,
    outputs: 2,
    align: "left",
    icon: 'modbus.png',
    paletteLabel: 'Modbus-Flex-Sequencer',
    //Node label
    label: function () {
      if (this.name) {
        return this.name
      } else if (this.sequences) {
        return "Modbus Flex Sequencer: " + this.sequences.length
      }
      return 'Modbus Flex Sequencer';
    },
    oneditprepare: function () {

      let tabs = RED.tabs.create({
        id: "node-input-modbus-tabs",
        onchange: function (tab) {
          $("#node-input-tabs-content").children().hide()
          $("#" + tab.id).show()
        }
      })

      tabs.addTab({
        id: "modbus-settings-tab",
        label: this._("modbus-contrib.tabs-label.settings")
      })

      tabs.addTab({
        id: "modbus-options-tab",
        label: this._("modbus-contrib.tabs-label.options")
      })

      if (this.delayOnStart) {
        $('#node-delay').show()
      }
      else {
        $('#node-delay').hide()
        $('#node-input-delayOnStart').prop('checked', false)
      }

      $('#node-input-delayOnStart').change(function () {
        if ($(this).is(':checked')) {
          $('#node-delay').show()
        } else {
          $('#node-delay').hide()
        }
      })

      let useIOFileCheckbox = $('#node-input-useIOFile')
      let modbusiofileRow = $('#modbusiofile-row')

      useIOFileCheckbox.change(function () {
        if ($(this).is(':checked')) {
          modbusiofileRow.show()
        } else {
          modbusiofileRow.hide()
        }
      })

      /*
          Polling Section
      */

      $('#node-input-sequences-container').css('min-width', '450px').editableList({
        addItem: function (row, index, data) {
          var sequence = data;

          if (!sequence.hasOwnProperty('fc')) {
            sequence = {
              name: null,
              unitid: null,
              fc: functions[0].v,
              address: null,
              quantity: null
            };
          }

          row.css({
            overflow: 'hidden',
            whiteSpace: 'nowrap'
          });

          let fragment = document.createDocumentFragment();
          var row0 = $('<div/>', {
              class: "form-row",
            })
            .appendTo(fragment);

          var row1 = $('<div/>', {
              class: "form-row",

            })
            .appendTo(fragment);

          var row2 = $('<div/>', {
              class: "form-row",
            })
            .appendTo(fragment);

          var row3 = $('<div/>', {
              class: "form-row",
            })
            .appendTo(fragment);

          var row4 = $('<div/>', {
              class: "form-row",
            })
            .appendTo(fragment);

          $('<label/>')
            .text("Name")
            .appendTo(row0);

          var sequenceNameProperty = $('<input/>', {
              class: "node-input-sequencename",
              type: "text",
              placeholder: "Sequence " + index
            })
            .appendTo(row0)

          $('<label/>')
            .text("Function")
            .appendTo(row1);

          var selectFunctionProperty = $('<select/>', {
              class: "node-input-function",
            })
            .appendTo(row1);

          for (var i = 0; i < 4; i++) {
            selectFunctionProperty.append($("<option></option>").val(functions[i].v).text(functions[i].t));
          }

          $('<label/>')
            .text("Unit ID")
            .appendTo(row2);

          var unitIdProperty = $('<input/>', {
              class: "node-input-unitid",
              type: "text",
            })
            .appendTo(row2)

          $('<label/>')
            .text("Address")
            .appendTo(row3);

          var addressProperty = $('<input/>', {
              class: "node-input-address",
              type: "text",
              placeholder: "0 - 65535"
            })
            .appendTo(row3)

          $('<label/>')
            .text("Quantity")
            .appendTo(row4);

          var quantityProperty = $('<input/>', {
              class: "node-input-quantity",
              type: "text",
              placeholder: "0 - 65535"
            })
            .appendTo(row4)

          sequenceNameProperty.val(sequence.name);
          selectFunctionProperty.val(sequence.fc);
          unitIdProperty.val(sequence.unitid);
          addressProperty.val(sequence.address);
          quantityProperty.val(sequence.quantity);

          selectFunctionProperty.change();

          row[0].appendChild(fragment);

        },
        removable: true,
        sortable: true
      })

      if (!this.sequences) {
        let sequenceMsg = {
          name: null,
          unitid: null,
          fc: functions[0].v,
          address: null,
          quantity: null
        };

        this.sequences = [sequenceMsg];
      }

      for (var i = 0; i < this.sequences.length; i++) {
        $("#node-input-sequences-container").editableList('addItem', this.sequences[i]);
      }
    },
    oneditsave: function () {
      var sequences = $("#node-input-sequences-container").editableList('items');
      var node = this;
      node.sequences = [];
      sequences.each(function (i) {
        var sequence = $(this);
        var r = {
          name: sequence.find(".node-input-sequencename").val(),
          unitid: sequence.find(".node-input-unitid").val(),
          fc: sequence.find(".node-input-function").val(),
          address: sequence.find(".node-input-address").val(),
          quantity: sequence.find(".node-input-quantity").val()
        };
        node.sequences.push(r);
      });
    },
    oneditresize: function (size) {

      var height = size.height;
      $('#node-input-sequences-container').css("height", (height - 145) + "px");

    }
  })
</script>


<script type="text/x-red" data-template-name="modbus-flex-sequencer">
  <div class="form-row">
    <label for="node-input-name"><i class="icon-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
    <input type="text" id="node-input-name" placeholder="Name">
  </div>

  <div class="form-row node-input-modbus-tabs-row">
    <ul style="min-width: 600px; margin-bottom: 20px;" id="node-input-modbus-tabs"></ul>
  </div>

  <div id="node-input-tabs-content">

    <div id="modbus-settings-tab" style="display:none">
      <div class="form-row" id="node-delayonstart">
          <label for="node-input-delayOnStart" style="width:160px">
          <i class="fa fa-power-off"></i> <span data-i18n="modbus-contrib.label.delayOnStart"></span></label>
          <input type="checkbox" id="node-input-delayOnStart" style="width:40px">
      </div>
      <div class="form-row" id="node-delay">
          <label for="node-input-startDelayTime"><i class="fa fa-clock-o"></i> <span data-i18n="modbus-contrib.label.startDelayTime"></span></label>
          <input type="text" id="node-input-startDelayTime" placeholder="10" style="width:80px">
      </div>
      <div class="form-row">
        <label for="node-input-server"><i class="icon-globe"></i> <span
            data-i18n="modbus-contrib.label.server"></span></label>
        <input type="text" id="node-input-server">
      </div>
      <div class="form-row node-input-sequences-container-row">
        <ol id="node-input-sequences-container"></ol>
      </div>
    </div>

    <div id="modbus-options-tab" style="display:none">
      <div class="form-row">
        <label style="min-width:190px" for="node-input-emptyMsgOnFail"><i class="fa fa-th"></i> <span
            data-i18n="modbus-contrib.label.emptyMsgOnFail"></span></label>
        <input type="checkbox" id="node-input-emptyMsgOnFail" style="max-width:30px">
      </div>
      <div class="form-row">
        <label style="min-width:190px" for="node-input-keepMsgProperties"><i class="fa fa-th"></i> <span
            data-i18n="modbus-contrib.label.keepMsgProperties"></span></label>
        <input type="checkbox" id="node-input-keepMsgProperties" style="max-width:30px">
      </div>
      <div class="form-row">
        <label style="min-width:190px" for="node-input-showStatusActivities"><i class="fa fa-th"></i> <span
            data-i18n="modbus-contrib.label.showActivities"></span></label>
        <input type="checkbox" id="node-input-showStatusActivities" style="max-width:30px">
      </div>
      <div class="form-row">
        <label style="min-width:190px" for="node-input-showErrors"><i class="fa fa-th"></i> <span
            data-i18n="modbus-contrib.label.showErrors"></span></label>
        <input type="checkbox" id="node-input-showErrors" style="max-width:30px">
      </div>
      <div class="form-row">
          <label style="min-width:190px" for="node-input-showWarnings"><i class="fa fa-th"></i> <span
          data-i18n="modbus-contrib.label.showWarnings"></span></label>
          <input type="checkbox" checked id="node-input-showWarnings" style="max-width:30px">
      </div>
      <hr>
      <div class="form-row">
        <label style="min-width:190px" for="node-input-useIOFile"><i class="fa fa-file-code-o"></i> <span
            data-i18n="modbus-contrib.label.useIOFile"></span></label>
        <input type="checkbox" id="node-input-useIOFile" style="max-width:30px">
      </div>
      <div id="modbusiofile-row">
        <div class="form-row">
          <label style="min-width:190px" for="node-input-logIOActivities"><i class="fa fa-th"></i> <span
              data-i18n="modbus-contrib.label.logIOActivities"></span></label>
          <input type="checkbox" id="node-input-logIOActivities" style="max-width:30px">
        </div>
        <div class="form-row">
          <label style="min-width:190px" for="node-input-useIOForPayload"><i class="fa fa-comment-o"></i> <span
              data-i18n="modbus-contrib.label.useIOForPayload"></span></label>
          <input type="checkbox" id="node-input-useIOForPayload" style="max-width:30px">
        </div>
        <div class="form-row">
          <label for="node-input-ioFile"><i class="icon-file"></i> <span
              data-i18n="modbus-contrib.label.ioFile"></span></label>
          <input type="text" id="node-input-ioFile">
        </div>
      </div>
    </div>
  </div>
</script>


<script type="text/x-red" data-help-name="modbus-flex-sequencer">
<p>
  Modbus TCP/Serial flexible input triggered auto-sequence read node with connection input parameters.
</p>
<p>
   Connects to a Modbus TPC or serial to read coils/inputs/registers automatically in sequence.
</p>
<p>
  Use any input to trigger the request but you can override the list using:
</p>
<code>
  msg.sequences
</code>
<p>
  <strong>Function codes (1:4) currently supported include:</strong>

    <ul>
      <li>FC 1: Read Coil Status</li>
      <li>FC 2: Read Input Status</li>
      <li>FC 3: Read Holding Registers</li>
      <li>FC 4: Read Input Registers</li>
    </ul>
    </p>
 <p>
     <strong>Input parameter for connecting Modbus</strong>
     <ul>
         <li>(mixed)  fc ('FC1', 'FC2', 'FC3', 'FC4' - Or - 1:4) - Function Code</li>
         <li>(integer) unitid (0..255 tcp | 0..247 serial) - Unit-ID</li>
         <li>(integer) address (0:65535) - The starting address</li>
         <li>(integer) quantity (1:65535) - The quantity of coils/inputs/registers to be read from the starting address</li>
     </ul>
 </p>

 <p>Output 1: data Array (PDU), modbus response Buffer, input message</p>
 <p>Output 2: modbus response Buffer, data Array (PDU), input message</p>

 <p>Function node code example for single input:</p>
 <code>
  msg.sequences = [
  {
      name : "Foo",
      fc : 1,
      address : 1,
      unitid: 2,
      quantity : 6,
  }]
 return msg;
 </code>

 <p>Function node code example for multiple inputs:</p>
 <code>
  msg.sequences = [
  {
      name : "Foo",
      fc : 1,
      address : 1,
      unitid: 2,
      quantity : 6,
  },
  {
      name : "Bar",
      fc : 3,
      address : 0,
      unitid: 1,
      quantity : 10
  },
  {
    name : "Baz",
    fc : 'FC4',
    address : 10,
    unitid: 3,
    quantity : 46
  }]
 return msg;
 </code>
  </script>
